package ElectricCraft.frames;

import ElectricCraft.model.ChipModel;
import ElectricCraft.model.ModelGroup;
import ElectricCraft.viewers.ChipEditor;
import ElectricCraft.viewers.ChipViewer;
import ElectricCraft.viewers.NotificationListener;

import javax.swing.*;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.LinkedList;
import java.util.List;

/**
 * ChipEditorPane class.
 */
public class ChipEditorPane extends ChipViewerPane {
    private final ChipModel originalModel;

	public ChipEditorPane(MainWindow parent, ChipModel model) {
        super(parent, new ChipModel(model));

        originalModel = model;

        initModel();

		initMouseListeners();
	}

    private boolean changed = false;
	private ChipEditor editor;

    public final boolean isChanged() {
        return changed;
    }

    private final List<NotificationListener> changedStatusListeners = new LinkedList<NotificationListener>();
    public void addChangedStatusListener(NotificationListener listener) {
        changedStatusListeners.add(listener);
    }

    private void notifyChangedStatus() {
        for (NotificationListener listener : changedStatusListeners) {
            listener.notified();
        }
    }

    @Override
    protected ChipViewer createViewer() {
        editor = new ChipEditor();
        return editor;
    }

    @Override
    protected boolean allowDragBy(MouseEvent event) {
        if (readonly && event.getButton() == MouseEvent.BUTTON1) {
            return true;
        }

        return event.getButton() == MouseEvent.BUTTON3;
    }

	private void initModel() {
        editor.setBrowseModelListener(new ChipEditor.BrowseModelListener() {
            public ChipModel browse() {
                return browseModel();
            }
        });

        editor.addChangeNotificationListener(
                new NotificationListener() {
                    public void notified() {
                        if (!changed) {
                            changed = true;
                            notifyChangedStatus();
                        }
                    }
                });
	}

    private ChipModel browseModel() {
        ModelSaveLoadFrame browser = new ModelSaveLoadFrame(parent, false);
        if (browser.wasCanceled()) {
            return null;
        }

        ModelGroup group = browser.getResultGroup();
        return group.findModelByName(browser.getResultName());
    }

	private void initMouseListeners() {
		MouseAdapter adapter = new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent mouseEvent) {
                if (readonly) {
                    return ;
                }

				getRootPane().getBounds(paneBounds);

                int x = mouseEvent.getX();
                int y = mouseEvent.getY();

				if (mouseEvent.getButton() == MouseEvent.BUTTON1) {
					editor.invertCross(x, y);
				}

                if (mouseEvent.getButton() == MouseEvent.BUTTON3) {
                    editor.startNewElementMode(x, y);
                }
			}

			private boolean isDeleteMode = false;

			@Override
			public void mousePressed(MouseEvent mouseEvent) {
                if (readonly || mouseEvent.isConsumed()) {
                    return ;
                }

				getRootPane().getBounds(paneBounds);

                int x = mouseEvent.getX();
                int y = mouseEvent.getY();

                if (!paneBounds.contains(x, y)) {
                    return ;
                }

				if (mouseEvent.getButton() == MouseEvent.BUTTON1) {
					if (mouseEvent.isControlDown()) {
						isDeleteMode = true;
						editor.clearElement(x, y);
					} else {
						editor.startWireDraw(x, y);
					}

                    mouseEvent.consume();
				}
			}

			@Override
			public void mouseReleased(MouseEvent mouseEvent) {
                if (readonly) {
                    return ;
                }

				editor.stopWireDraw();
				isDeleteMode = false;

				getRootPane().getBounds(paneBounds);

				int x = mouseEvent.getX();
				int y = mouseEvent.getY();

				if (mouseEvent.getButton() == MouseEvent.BUTTON1) {
					if (editor.isNewElementMode()) {
						editor.chooseNewElement(x, y);
					}
				}
			}

            private void processMove(MouseEvent mouseEvent) {
                if (readonly) {
                    return ;
                }

                getRootPane().getBounds(paneBounds);
                int x = mouseEvent.getX();
                int y = mouseEvent.getY();

                if (isDeleteMode && mouseEvent.isControlDown()) {
                    editor.clearElement(x, y);
                }

                if (editor.isWireDrawStarted()) {
                    editor.updateWireDraw(x, y);
                }
            }

			@Override
			public void mouseMoved(MouseEvent mouseEvent) {
                processMove(mouseEvent);
			}

			@Override
			public void mouseDragged(MouseEvent mouseEvent) {
                processMove(mouseEvent);
			}
		};

		addMouseListener(adapter);
		addMouseMotionListener(adapter);
	}

    public void save() {
        ChipModel rootModel = modelPath.getRoot();

        rootModel.copy(originalModel);

        changed = false;
        notifyChangedStatus();
    }

    public final ChipModel getOriginalModel() {
        return originalModel;
    }

    public boolean processClose() {
        // just closed unchanged window
        if (!changed) {
            return true;
        }

        parent.setCurrentEditor(this);

        int res = JOptionPane.showConfirmDialog(
                parent.getFrame(), "Do you want to save your changes?", "File was changed", JOptionPane.YES_NO_CANCEL_OPTION);

        if (res == JOptionPane.CANCEL_OPTION) {
            return false;
        }

        if (res == JOptionPane.NO_OPTION) {
            return true;
        }

        save();

        return parent.savePackage(false);
    }

    private boolean readonly = false;
    public void setReadonly(boolean enable) {
        readonly = enable;
    }

    public void updateModelName() {
        modelPath.getRoot().name = originalModel.name;
    }
}